/*
The current keys:

left/right - decrease/increase gravity
up/down - increase/decrease velocity
g - gravity on/off
e - elastic collisions on/off
r - time slow
t - time speed up
f - line circle/filled circle
w - wrapped on/off
c - collision on/off
n - add a ball
m - delete a ball

*/
/*
Written by Matt Altermatt (matthew.altermatt@colorado.edu) starting around 22 April 2001.
The framework to build and handle the OpenGL and Windows routines was take from
NeHe's site (nehe.gamedev.net).
Thanks goes to BJ and Whitney for giving me ideas and feedback on what to do.
Also, thanks to Clint for letting me borrow his physics book.  I kind of forgot all the
equations after a year.

This code is totally free, and comes with no guarantees or warranties.  It was written
to combat boredom.  If you do use any of it in your code, or mod it please send me a
copy or let me know.  I am interested to see if and where this code ends up.
*/

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>

//////////////////////////////////////////////////////////////////////////////
//                           Added header files.                            //
//////////////////////////////////////////////////////////////////////////////

#include <math.h>
#include <time.h>
#include "list.h"

//////////////////////////////////////////////////////////////////////////////

HGLRC hRC = NULL;
HDC hDC = NULL;
HWND hWnd = NULL;
HINSTANCE hInstance;

bool keys[256];
bool active = TRUE;
bool fullscreen = TRUE;

//////////////////////////////////////////////////////////////////////////////
//                    Added constants and variables.                        //
//////////////////////////////////////////////////////////////////////////////

const double PI = 3.14159;
const double G = 6.67e-11;
const double MIN_RADIUS = 0.01;
const double MAX_RADIUS = 0.05;
const double MIN_VELOCITY = 0.0;
const double MAX_VELOCITY = 0.04;
const int MAX_NUMBER_BALLS_CREATED = 5;

bool filled = TRUE;
bool addnewcircle = FALSE;
bool wrap = FALSE;
bool gravity = TRUE;
bool collision = FALSE;
bool elastic = TRUE;
bool deletecircle = FALSE;

double fTime = 0.5;
double altervelocity = 0.0;
double amount_gravity = 185.0;

//////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
double random_fraction()
------------------------------------------------------------------------------
Returns a random fraction by dividing a random number by the maximum amount
of a random number.
*****************************************************************************/
double random_fraction() {
	return rand() / double( RAND_MAX );
}

/*****************************************************************************
float random_real( double, double )
------------------------------------------------------------------------------
Returns a number that is equal or between the low and the high numbers which
are specified.
*****************************************************************************/
double random_real( double low, double high ) {
	return low + random_fraction() * ( high - low );
}

/*****************************************************************************
Circle *InitCircle()
------------------------------------------------------------------------------
Returns a newly created circle from the specifications listed below.

The majority of the specs are not set in stone and can be altered to make
it look better to the programmer or user.
*****************************************************************************/
Circle *InitCircle( ) {

	// The ranges the values should be :
	// x: radius ... 2 - radius
	// y: radius ... 2 - radius
	// vel_x: 0.0 ... 0.01
	// vel_y: 0.0 ... 0.01
	// mass: radius * 1e6
	// radius: .01 ... .05
	// color_r: 0 ... 1
	// color_g: 0 ... 1
	// color_b: 0 ... 1
	
	Circle *circle = new Circle;

	circle->radius = random_real( MIN_RADIUS, MAX_RADIUS );
	circle->mass = circle->radius * 1e6;
	circle->x = random_real( circle->radius, ( 2 - circle->radius ) );
	circle->y = random_real( circle->radius, ( 2 - circle->radius ) );
	circle->vel_x = random_real( MIN_VELOCITY, MAX_VELOCITY );
	circle->vel_y = random_real( MIN_VELOCITY, MAX_VELOCITY );
	circle->color_r = random_real( 0, 1 );
	circle->color_g = random_real( 0, 1 );
	circle->color_b = random_real( 0, 1 );
	
	if( rand() % 2 == 0 ) {
		circle->vel_x *= -1;
	}
	
	if( rand() %2 == 0 ) {
		circle->vel_y *= -1;
	}
	
	return circle;
}

/*****************************************************************************
void DrawCircles( const List )
------------------------------------------------------------------------------
Runs from head to the tail drawing each circle in order that it is on the
list.
*****************************************************************************/
void DrawCircles( const List circles ) {
	if( circles.head == 0 ) {
		return;
	}
	int x;
	Circle *walker = circles.head;

	while( walker != 0 ) {
		glPushMatrix();
		glTranslated( walker->x, walker->y, 0.0f );
		glColor4d( walker->color_r, walker->color_g, walker->color_b, 0.5 );
		
		if( filled ) {
			glBegin( GL_TRIANGLE_FAN );
		}
		else {
			glBegin( GL_LINE_LOOP );
		}
		
		for( x = 0; x < 360; x += 20 ) {
			glVertex3d( cos( x * PI / 180 ) * walker->radius, sin( x * PI / 180 ) * walker->radius, 0.0 );
		}
		glEnd();
		glPopMatrix();

		walker = walker->next;
	}
}

/*****************************************************************************
void Boundary( Circle * )
------------------------------------------------------------------------------
Checking to see if the ball went out of bounds, if so either rebound it off,
or allow it to wrap depending on the setting.
*****************************************************************************/
void Boundary( Circle *circle ) {
	// The ball is placed back inside the screen, then the vel is reversed.
	if( wrap ) {  // elastic walls
		if( circle->x > 2 - circle->radius ) {
			circle->x = 2 - circle->radius;
			circle->vel_x = -1 * circle->vel_x;
		}
		else if( circle->x <= circle->radius ) {
			circle->x = circle->radius;
			circle->vel_x = -1 * circle->vel_x;
		}
		
		if( circle->y > 2 - circle->radius ) {
			circle->y = 2 - circle->radius;
			circle->vel_y = -1 * circle->vel_y;
		}
		else if( circle->y <= circle->radius ) {
			circle->y = circle->radius;
			circle->vel_y = -1 * circle->vel_y;
		}
		// The ball just wraps around, but it does it at MAX_RADIUS instead of the ball's radius
		// to make it look better when two balls of differents size are wrapped.
	}
	else {  // no walls
		if( circle->x > 2 + MAX_RADIUS ) {
			circle->x = -1 * MAX_RADIUS;
		}
		else if( circle->x < -1 * MAX_RADIUS ) {
			circle->x = 2 + MAX_RADIUS;
		}
		
		if( circle->y > 2 + MAX_RADIUS ) {
			circle->y = -1 * MAX_RADIUS;
		}
		else if( circle->y <= -1 * MAX_RADIUS ) {
			circle->y = 2 + MAX_RADIUS;
		}
	}
}

/*****************************************************************************
void AlterVelocity( Circle * )
------------------------------------------------------------------------------
This is increase or decrease a certain amount of velocity from each ball.
*****************************************************************************/
void AlterVelocity( Circle *circle ) {
	if( circle->vel_x < 0 ) {
		circle->vel_x = circle->vel_x - altervelocity;
	}
	else if( circle->vel_x > 0 ) {
		circle->vel_x = circle->vel_x + altervelocity;
	}
	if( circle->vel_y < 0 ) {
		circle->vel_y = circle->vel_y - altervelocity;
	}
	else if( circle->vel_y > 0 ) {
		circle->vel_y = circle->vel_y + altervelocity;
	}
}


/*****************************************************************************
void Gravity( const List )
------------------------------------------------------------------------------
Walks through the list vector adding the force of each ball to the current
ball's velocity.
*****************************************************************************/
void Gravity( const List circles ) {
	double total_force, force_x, force_y, delta_x, delta_y, theta_rad, hypo;
	Circle *walker = circles.head;
	Circle *second_walker = circles.head;

	while( walker ) {
		second_walker = walker->next;
		while( second_walker ) {
			delta_x = second_walker->x - walker->x;
			delta_y = second_walker->y - walker->y;
			hypo = sqrt( delta_x * delta_x + delta_y * delta_y );
			if( hypo == 0 ) {
				hypo = 0.0000000001;
			}
			theta_rad = atan( delta_y / delta_x );

			// This is the equation: F = G * ( m1 * m2 ) / r^2.
			// The last r is taken out though, it looks a lot better only divided by r.
			// The amount_gravity is added to increase/decrease the gravity.  If decreased
			// enough it acts as if each ball is a repelling magnet.
			total_force = G * ( walker->mass * second_walker->mass * amount_gravity ) / ( hypo );
			force_x = cos( theta_rad ) * total_force;
			force_y = sin( theta_rad ) * total_force;			
			
			if( ( delta_x > 0 && delta_y > 0 ) || ( delta_x > 0 && delta_y < 0 ) ) { // upper right and bottom right
				walker->vel_x += ( force_x / walker->mass ) * fTime;
				walker->vel_y += ( force_y / walker->mass ) * fTime;
				second_walker->vel_x -= ( force_x / second_walker->mass ) * fTime;
				second_walker->vel_y -= ( force_y / second_walker->mass ) * fTime;
			}
			else if( ( delta_x < 0 && delta_y > 0 ) || ( delta_x < 0 && delta_y < 0 ) ) {  // upper left and bottom left
				walker->vel_x -= ( force_x / walker->mass ) * fTime;
				walker->vel_y -= ( force_y / walker->mass ) * fTime;
				second_walker->vel_x += ( force_x / second_walker->mass ) * fTime;
				second_walker->vel_y += ( force_y / second_walker->mass ) * fTime;
			}
			second_walker = second_walker->next;
		}
		walker = walker->next;
	}
}

/*****************************************************************************
void CheckVelocity( Circle * )
------------------------------------------------------------------------------
Make sure that none of the balls gets too out of control.
*****************************************************************************/
void CheckVelocity( Circle * circle ) {
	if( circle->vel_x > MAX_VELOCITY ) {
		circle->vel_x = MAX_VELOCITY;
	}
	else if( circle->vel_x < -1.0 * MAX_VELOCITY ) {
		circle->vel_x = -1.0 * MAX_VELOCITY;
	}

	if( circle->vel_y > MAX_VELOCITY ) {
		circle->vel_y = MAX_VELOCITY;
	}
	else if( circle->vel_y < -1.0 * MAX_VELOCITY ) {
		circle->vel_y = -1.0 * MAX_VELOCITY;
	}
}

/*****************************************************************************
void ElasticCollision( Circle *, Circle * )
------------------------------------------------------------------------------
Conservation of Momentum: m1v1 + m2v2 = m1v1' + m2v2'
Head-on Elastic Collision: v1 - v2 = v2' - v1'
Two equations yield the 4 unknowns( 2 for x and 2 for y ), which are then
applied to the current circles i and j.
*****************************************************************************/
void ElasticCollision( Circle *circle1, Circle *circle2 ) {
	double new_vel_x1, new_vel_x2, new_vel_y1, new_vel_y2;
	new_vel_x1 = ( circle1->mass * circle1->vel_x + circle2->mass * circle2->vel_x -
		circle2->mass * circle1->vel_x + circle2->mass * circle2->vel_x ) /
		( circle1->mass + circle2->mass );
	new_vel_x2 = circle1->vel_x - circle2->vel_x + new_vel_x1;
	
	new_vel_y1 = ( circle1->mass * circle1->vel_y + circle2->mass * circle2->vel_y -
		circle2->mass * circle1->vel_y + circle2->mass * circle2->vel_y ) /
		( circle1->mass + circle2->mass );
	new_vel_y2 = circle1->vel_y - circle2->vel_y + new_vel_y1;
	
	circle1->vel_x = new_vel_x1;
	circle1->vel_y = new_vel_y1;
	circle2->vel_x = new_vel_x2;
	circle2->vel_y = new_vel_y2;
}

/*****************************************************************************
void InElasticCollision( Circle *, Circle * )
------------------------------------------------------------------------------
Using the equation m1v1 + m2v2 = (m1 + m2) v'
*****************************************************************************/
void InElasticCollision( Circle *circle1, Circle *circle2 ) {
	double new_vel_x, new_vel_y;
	new_vel_x = ( ( circle1->mass * circle1->vel_x ) + ( circle2->mass * circle2->vel_x ) ) /
		( circle1->mass + circle2->mass );
	new_vel_y = ( ( circle1->mass * circle1->vel_y ) + ( circle2->mass * circle2->vel_y ) ) /
		( circle1->mass + circle2->mass );

	circle1->vel_x = new_vel_x;
	circle1->vel_y = new_vel_y;
	circle2->vel_x = new_vel_x;
	circle2->vel_y = new_vel_y;
}

/*****************************************************************************
void KeepOutside( Circle *, Circle * )
------------------------------------------------------------------------------
This will make it so that one ball will not be able to occupy the space of
another ball.  This makes the elastic/inelastic collisions look a lot better.
*****************************************************************************/
void KeepOutside( Circle *circle1, Circle *circle2 ) {
	double theta_rad, delta_x, delta_y;
	delta_x = circle1->x - circle2->x;  // distance in x and y that i is from j
	delta_y = circle1->y - circle2->y;
				
	// The theta of the triangle formed by i and j, always assuming that y is opposite and x is adjacent.
	theta_rad = atan( delta_y / delta_x );
				
	if( ( delta_x > 0 && delta_y > 0 ) || ( delta_x > 0 && delta_y < 0 ) ) { // upper right and bottom right
		circle1->x = circle2->x + ( cos( theta_rad ) * ( circle1->radius + circle2->radius ) );
		circle1->y = circle2->y + ( sin( theta_rad ) * ( circle1->radius + circle2->radius ) );
	}
	else if( ( delta_x < 0 && delta_y > 0 ) || ( delta_x < 0 && delta_y < 0 ) ) {  // upper left and bottom left
		circle1->x = circle2->x - ( cos( theta_rad ) * ( circle1->radius + circle2->radius ) );
		circle1->y = circle2->y - ( sin( theta_rad ) * ( circle1->radius + circle2->radius ) );
	}
	else if( delta_y == 0 ) { // both i and j are on the same x
		if( delta_x < 0 ) { // i is to the left of j
			circle1->x = circle2->x - ( circle1->radius + circle2->radius ) - 0.01f;
		}
		else { // i is to the right of j
			circle1->x = circle2->x + ( circle1->radius + circle2->radius ) + 0.01f;
		}
	}
	else {  // (delta_x == 0 ) both i and j are on the same y
		if( delta_y < 0 ) { // i is below j
			circle1->y = circle2->y - ( circle1->radius + circle2->radius ) - 0.01f;
		}
		else {  // i is is above j
			circle1->y = circle2->y + ( circle1->radius + circle2->radius ) - 0.01f;
		}
	}
}

/*****************************************************************************
void MoveCircles( List * )
------------------------------------------------------------------------------
First it checks if there is gravity affecting the movements of the balls.
Then it starts walking through the list.  It checks to see if the velocity is
being altered.  Then it moves the balls.  Then it checks the boundaries on the
circle.  Then it will see if one of the balls is inside, or hitting another
one.  If so it runs the correct routine.  Then it moves the walker along, then
make the velocity to 0.
*****************************************************************************/
void MoveCircles( List *circles ) {

	if( gravity ) {
		Gravity( *circles );
	}

	Circle *walker = circles->head;
	Circle *second_walker = circles->head;

	while( walker != 0 ) {
		// If the up or down arrow is pressed than the velocity should be altered for each circle.
		// If the up or down arrow is not pressed than it skips AlterVelocity.

		if( altervelocity != 0 ) {
			AlterVelocity( walker );
		}
		
		CheckVelocity( walker );

		// Routine to move the circles.
		// Using new = old + velocity * fTime + 1/2
		if( walker->vel_x < 0 ) {
			walker->x = walker->x + walker->vel_x * fTime;
		}
		else if( walker->vel_x > 0 ){
			walker->x = walker->x + walker->vel_x * fTime;
		}
		
		if( walker->vel_y < 0 ) {
			walker->y = walker->y + walker->vel_y * fTime;
		}
		else if( walker->vel_y > 0 ){
			walker->y = walker->y + walker->vel_y * fTime;
		}
		
		Boundary( walker );
		
		if ( collision ) {
			second_walker = circles->head;

			// Looping through each ball (second_walker) to see if the current ball (walker) is touching or inside of it.
			while( second_walker ) {
				if( walker != second_walker && sqrt( pow( walker->x - second_walker->x, 2 )
					+ pow( walker->y - second_walker->y, 2 ) ) <= walker->radius + second_walker->radius ) {
					
					if( collision ) {
						if( elastic ) {
							ElasticCollision( walker, second_walker );
						}
						else {  // non-elastic
							InElasticCollision( walker, second_walker );
						}
						
						// Code to not have the two balls inside of each other.
						KeepOutside( walker, second_walker );
					}
									}
				second_walker = second_walker->next;
			}
		}	
		walker = walker->next;
	}
	altervelocity = 0.0f;
}


int DrawGLScene( GLvoid ) {
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glLoadIdentity();
	// Translated it over by -1 in the x and y to never have to worry about negative numbers.
	glTranslatef( -1.0f, -1.0f, 0.0f );
	static List circles;
	static bool hasran = FALSE;

	if( !hasran ) {
		circles.InsertNode( InitCircle() );
		circles.InsertNode( InitCircle() );
		circles.InsertNode( InitCircle() );
		circles.InsertNode( InitCircle() );
		hasran = TRUE;
	}

	if( addnewcircle ) {
		circles.InsertNode( InitCircle() );
		addnewcircle = FALSE;
	}

	if( deletecircle ) {
		circles.DeleteNode( circles.tail );
		deletecircle = FALSE;
	}

	MoveCircles( &circles );
	DrawCircles( circles );
	
	return TRUE;
}

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

GLvoid ReSizeGLScene( GLsizei width, GLsizei height ) {
	if( height == 0 ) {
		height = 1;
	}

	glViewport( 0, 0, width, height );
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
}

int InitGL( GLvoid ) {
/*  Due to this being in 2d, all of this is not needed, and may increase performance.
	glShadeModel( GL_SMOOTH );
	glClearDepth( 1.0f );
	glEnable( GL_DEPTH_TEST );
	glDepthFunc( GL_LEQUAL );
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
*/
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);
	glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); // background color
	srand( time( 0 ) );
	return TRUE;
}

GLvoid KillGLWindow( GLvoid ) {
	if( fullscreen ) {
		ChangeDisplaySettings( NULL, 0 );
		ShowCursor( TRUE );
	}

	if( hRC ) {
		if( !wglMakeCurrent( NULL, NULL ) ) {
			MessageBox( NULL, "Release of DC and RC Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION );
		}
		if( !wglDeleteContext( hRC ) ) {
			MessageBox( NULL, "Release Rendering Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION );
		}

		hRC = NULL;
	}

	if( hDC && !ReleaseDC( hWnd, hDC ) ) {
		MessageBox( NULL, "Release Device Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION );
		hDC = NULL;
	}

	if( hWnd && !DestroyWindow( hWnd ) ) {
		MessageBox( NULL, "Could Not Release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION );
		hWnd = NULL;
	}

	if( !UnregisterClass( "OpenGL", hInstance ) ) {
		MessageBox( NULL, "Could Not Unregister Class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION );
		hInstance = NULL;
	}
}

BOOL CreateGLWindow( char* title, int width, int height, int bits, bool fullscreenflag ) {
	GLuint PixelFormat;
	WNDCLASS wc;
	DWORD dwExStyle;
	DWORD dwStyle;
	RECT WindowRect;

	WindowRect.left = (long)0;
	WindowRect.right = (long)width;
	WindowRect.top = (long)0;
	WindowRect.bottom = (long)height;
	
	fullscreen = fullscreenflag;

	hInstance = GetModuleHandle( NULL );
	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc = (WNDPROC)WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon( NULL, IDI_WINLOGO );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = NULL;
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "OpenGL";

	if( !RegisterClass( &wc ) ) {
		MessageBox( NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	if( fullscreen ) {
		DEVMODE dmScreenSettings;
		memset( &dmScreenSettings, 0, sizeof( dmScreenSettings ) );
		dmScreenSettings.dmSize = sizeof( dmScreenSettings ) ;
		dmScreenSettings.dmPelsWidth = width;
		dmScreenSettings.dmPelsHeight = height;
		dmScreenSettings.dmBitsPerPel = bits;
		dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
		if( ChangeDisplaySettings( &dmScreenSettings, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL ) {
			if( MessageBox( NULL, "The Requested Fullscreen Mode Is Not Supported By\nYourVideoCard.  Use Windowed Mode Instead?", "ERROR", MB_YESNO | MB_ICONEXCLAMATION ) == IDYES ) {
				fullscreen = FALSE;
			}
			else {
				MessageBox( NULL, "Program Will Now Close.", "ERROR", MB_OK | MB_ICONSTOP );
				return FALSE;
			}
		}
	}

	if( fullscreen ) {
		dwExStyle = WS_EX_APPWINDOW;
		dwStyle = WS_POPUP;
		ShowCursor( FALSE );
	}
	else {
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle = WS_OVERLAPPEDWINDOW;
	}

	AdjustWindowRectEx( &WindowRect, dwStyle, FALSE, dwExStyle );

	if( !( hWnd = CreateWindowEx( dwExStyle, "OpenGL", title, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwStyle, 0, 0, 
		                          WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top,
								  NULL, NULL, hInstance, NULL ) ) ) {
		KillGLWindow();
		MessageBox( NULL, "Window Creation Error.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	static PIXELFORMATDESCRIPTOR pfd = {
		sizeof( PIXELFORMATDESCRIPTOR ), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
	    PFD_TYPE_RGBA, bits, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0
	};

	if( !( hDC = GetDC( hWnd ) ) ) {
		KillGLWindow();
		MessageBox( NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	if( !( PixelFormat = ChoosePixelFormat( hDC, &pfd ) ) ) {
		KillGLWindow();
		MessageBox( NULL, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	if( !SetPixelFormat( hDC, PixelFormat, &pfd ) ) {
		KillGLWindow();
		MessageBox( NULL, "Can't Set The PixelFormat.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	if( !( hRC = wglCreateContext( hDC ) ) ) {
		KillGLWindow();
		MessageBox( NULL, "Can't Create A GL Rendering Context.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	if( !wglMakeCurrent( hDC, hRC ) ) {
		KillGLWindow();
		MessageBox( NULL, "Can't Activate The GL Rendering Context.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	ShowWindow( hWnd, SW_SHOW );
	SetForegroundWindow( hWnd );
	SetFocus( hWnd );
	ReSizeGLScene( width, height );

	if( !InitGL() ) {
		KillGLWindow();
		MessageBox( NULL, "Initialization Failed.", "ERROR", MB_OK | MB_ICONINFORMATION );
		return FALSE;
	}

	return TRUE;
}	

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
	switch( uMsg ) {
	case WM_ACTIVATE: 
		{
			if( !HIWORD( wParam ) ) {
				active = TRUE;
			}
			else {
				active = FALSE;
			}

			return 0;
		}

	case WM_SYSCOMMAND:
		{
			switch( wParam ) {
			case SC_SCREENSAVE:
			case SC_MONITORPOWER:
				return 0;
			}
			break;
		}

	case WM_CLOSE:
		{
			PostQuitMessage( 0 );
			return 0;
		}

	case WM_KEYDOWN:
		{
			keys[wParam] = TRUE;
			return 0;
		}

	case WM_KEYUP:
		{
			keys[wParam] = FALSE;
			return 0;
		}

	case WM_SIZE:
		{
			ReSizeGLScene( LOWORD( lParam ), HIWORD( lParam ) );
			return 0;
		}
	}

	return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
	MSG msg;
	BOOL done = FALSE;

	if( MessageBox( NULL, "Would you like to run in fullscreen mode?", "Start Fullscreen?", MB_YESNO | MB_ICONQUESTION ) == IDNO ) {
		fullscreen = FALSE;
	}

	if( !CreateGLWindow( "Balls", 1024, 768, 16, fullscreen ) ) {  // title and size of window
		return 0;
	}

	while( !done ) {
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
			if( msg.message == WM_QUIT ) {
				done = TRUE;
			}
			else {
				TranslateMessage( &msg );
				DispatchMessage( &msg );
			}
		}
		else {
			if( active ) {
				if( keys[VK_ESCAPE] ) {
					done = TRUE;
				}
				else {
					DrawGLScene();
					SwapBuffers( hDC );
				}
			}

			if( keys[VK_F1] ) {
				keys[VK_F1] = FALSE;
				KillGLWindow();
				fullscreen = !fullscreen;
				if( !CreateGLWindow( "Balls", 1024, 768, 16, fullscreen ) ) {  //title and size of window, again
					return 0;
				}
			}
			
			if( keys['N'] ) {
				keys['N'] = FALSE;
				addnewcircle = TRUE;
			}
			if( keys['M'] ) {
				keys['M'] = FALSE;
				deletecircle = TRUE;
			}
			if( keys['W'] ) {
				keys['W'] = FALSE;
				wrap = !wrap;
			}
			if( keys[VK_UP] ) {
				keys[VK_UP] = FALSE;
				altervelocity = 0.001f;
			}
			if( keys[VK_DOWN] ) {
				keys[VK_DOWN] = FALSE;
				altervelocity = -0.001f;
			}
			if( keys['G'] ) {
				keys['G'] = FALSE;
				gravity = !gravity;
			}
			if( keys[VK_LEFT] ) {
				keys[VK_LEFT] = FALSE;
				amount_gravity -= 1.0f;
			}
			if( keys[VK_RIGHT] ) {
				keys[VK_RIGHT] = FALSE;
				amount_gravity += 1.0f;
			}
			if( keys['E'] ) {
				keys['E'] = FALSE;
				elastic = !elastic;
			}
			if( keys['C'] ) {
				keys['C'] = FALSE;
				collision = !collision;
			}
			if( keys['F'] ) {
				keys['F'] = FALSE;
				filled = !filled;
			}
			if( keys['T'] ) {
				keys['T'] = FALSE;
				fTime += 0.1;
				if( fTime > 2 ) {
					fTime = 2;
				}
			}
			if( keys['R'] ) {
				keys['R'] = FALSE;
				fTime -= 0.1;
				if( fTime < 0.1 ) {
					fTime = 0.1;
				}
			}
		}
	}

	KillGLWindow();
	return( msg.wParam );
}
